package com.jtcl.config;

import com.jtcl.enums.OrderEvent;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.GenericTypeResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;

/**
 * @author sjt
 * @since 2021-02-26 11:57
 */

/**
 * 通过Spring创建StateMachineBuilder实例，通过buidler创建状态机(单例)
 * 创建无类型化状态机，简化状态机，防止过多泛化导致代码不易阅读(因为不太理解一些高级的使用)
 */
public abstract class AbstractStateMachineEngine<T extends UntypedStateMachine> implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    protected UntypedStateMachineBuilder stateMachineBuilder = null;

    @SuppressWarnings("unchecked")
    public AbstractStateMachineEngine() {
        //识别泛型参数
        Class<T> genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(),
                AbstractStateMachineEngine.class);
        stateMachineBuilder = StateMachineBuilderFactory.create(genericType, ApplicationContext.class);
    }

    //注入applicationContext，并在创建StateMachine实例时注入
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 可以通过向OrderContext 上下文传递一些业务参数，比如orderId等等
     */
    public boolean fire(OrderEvent event, OrderContext context) {
        T fsm = stateMachineBuilder.newUntypedStateMachine(
                context.getOrderDTO().getState(),
                applicationContext);

        //由于StateMachine实例不是由Spring容器创建，所以这个过程中无法通过注解方式开启事务(Spring没有机会去创建事务代理)，因此采用了编程式事务
        DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) applicationContext.getBean("transactionManager");
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            fsm.fire(event, context);
            transactionManager.commit(status);
            //这里会返回状态机是否出错，如果出错可用于通知Controller层，目前只会这样简单的操作
            return fsm.isError();
        } catch (Exception ex) {
            //用于事务回滚
            transactionManager.rollback(status);
            return true;
        }
    }
}